import applyMixin from './mixin';
import { assert ,forEachValue,isObject} from './util'
import ModuleCollection from './module/module-collection.js'

//保存全局的Vue
let Vue;
/**
 * 判断是否处于浏览器环境,且是否实例化过vue
 * 执行install方法
 */
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue);
}

/**
 * 实现store的构造函数
 */
export class Store {
  /**
   * option就是传入的state,getter,mdoules...
   */
  constructor(options = {}){
    // 自动安装如果它没有完成，并且`窗口`有`vue`。
    // 在某些情况下允许用户避免自动安装，
    // 应该在这里放置此代码。 查看＃731.
    if (!Vue && typeof window !== 'undefined' && window.Vue) {
      install(window.Vue)
    }
    // if (__DEV__) {
    //   assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
    //   assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
    //   assert(this instanceof Store, `store must be called with the new operator.`)
    // }
    const {
      plugins = [],
      strict = false
    } = options
    // // 数据初始化、module树构造
    this._committing = false // 是否在进行提交状态标识
    this._actions = Object.create(null) // acitons操作对象
    this._mutations = Object.create(null) // mutations操作对象
    this._wrappedGetters = Object.create(null) // 封装后的getters集合对象
    this._modules = new ModuleCollection(options) // Vuex支持store分模块传入，存储分析后的modules
    this._modulesNamespaceMap = Object.create(null) // 模块命名空间map
    this._subscribers = [] // 订阅函数集合，Vuex提供了subscribe功能
    this._watcherVM = new Vue() // Vue组件用于watch监视变化

    const store = this
    const { dispatch, commit } = this;
    //挂载dispatch与commit
    this.dispatch = function boundDispatch (type, payload) {
      //这里改变this指向,调用函数
      return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit (type, payload, options) {
      //this执行store实例
      return commit.call(store, type, payload, options)
    }

    //未知,用户传入的
    this.strict = strict;
    //取出根state
    const state = this._modules.root.state
    //这个作者的注释已经写得挺明白，就是初始化根模块，递归注册子模块，收集getter
    installModule(this, state, [], this._modules.root);
    //初始化store中的state,使得state变成响应式的，原理就是将state作为一个vue实例的data属性传入
    resetStoreVM(this, state)
  }
  get state(){
    //这里获取new vue的响应式数据
    return this._vm._data.$$state;
  }
  set state (v) {
    assert(false, `use store.replaceState() to explicit replace store state.`)
  }
  //可以触发mutation修改对state的修改，并且是同步执行的
  commit (_type, _payload, _options) {
    // 首先统一传入参数的格式，主要是针对当type是个对象的情况，需要把这个对象解析出来
    const {
      type,
      payload,
      options
    } = unifyObjectStyle(_type, _payload, _options);

    const mutation = { type, payload }
    const entry = this._mutations[type];
    if (!entry) {
      if (__DEV__) {
        console.error(`[vuex] unknown mutation type: ${type}`)
      }
      return
    }
    // 在_withCommit中触发上面获取的mutation函数，简单粗暴使用数组的forEach执行哈哈，之
    //所以要在外面包一层_withCommit，是表明操作的同步性
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })
    // 这个就是之前说的监听队列，在每次执行commit函数时都会遍历执行一下这个队列
    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach(sub => sub(mutation, this.state))
    if (
      __DEV__ &&
      options && options.silent
    ) {
      console.warn(
        `[vuex] mutation type: ${type}. Silent option has been removed. ` +
        'Use the filter functionality in the vue-devtools'
      )
    }
  }

  dispatch (_type, _payload) {
    // check object-style dispatch
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload)

    const action = { type, payload }
    const entry = this._actions[type]
    if (!entry) {
      if (__DEV__) {
        console.error(`[vuex] unknown action type: ${type}`)
      }
      return
    }

    try {
      this._actionSubscribers
        .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
        .filter(sub => sub.before)
        .forEach(sub => sub.before(action, this.state))
    } catch (e) {
      if (__DEV__) {
        console.warn(`[vuex] error in before action subscribers: `)
        console.error(e)
      }
    }

    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)

    return new Promise((resolve, reject) => {
      result.then(res => {
        try {
          this._actionSubscribers
            .filter(sub => sub.after)
            .forEach(sub => sub.after(action, this.state))
        } catch (e) {
          if (__DEV__) {
            console.warn(`[vuex] error in after action subscribers: `)
            console.error(e)
          }
        }
        resolve(res)
      }, error => {
        try {
          this._actionSubscribers
            .filter(sub => sub.error)
            .forEach(sub => sub.error(action, this.state, error))
        } catch (e) {
          if (__DEV__) {
            console.warn(`[vuex] error in error action subscribers: `)
            console.error(e)
          }
        }
        reject(error)
      })
    })
  }
}
function makeLocalContext (store, namespace, path) {
  const noNamespace = namespace === '';
  //local对象创建dispath,commit
  const local = {
    dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
      const args = unifyObjectStyle(_type, _payload, _options)
      const { payload, options } = args
      let { type } = args

      if (!options || !options.root) {
        type = namespace + type
        if (__DEV__ && !store._actions[type]) {
          console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`)
          return
        }
      }

      return store.dispatch(type, payload)
    },

    commit: noNamespace ? store.commit : (_type, _payload, _options) => {
      const args = unifyObjectStyle(_type, _payload, _options)
      const { payload, options } = args
      let { type } = args

      if (!options || !options.root) {
        type = namespace + type
        if (__DEV__ && !store._mutations[type]) {
          console.error(`[vuex] unknown local mutation type: ${args.type}, global type: ${type}`)
          return
        }
      }

      store.commit(type, payload, options)
    }
  }

  // getters and state object must be gotten lazily
  // because they will be changed by vm update
  //这里属性代理,添加getters和state
  Object.defineProperties(local, {
    getters: {
      get: noNamespace
        ? () => store.getters
        : () => makeLocalGetters(store, namespace)
    },
    state: {
      get: () => getNestedState(store.state, path)
    }
  })

  return local
}
function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length
  //命名空间
  const namespace = store._modules.getNamespace(path)

  // 命名空间中
  if (module.namespaced) {

    if (store._modulesNamespaceMap[namespace] && __DEV__) {
      console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
    }
    store._modulesNamespaceMap[namespace] = module
  }
  //设定state

  if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1))
    const moduleName = path[path.length - 1];
    //store实例上
    store._withCommit(() => {
      if (__DEV__) {
        if (moduleName in parentState) {
          console.warn(
            `[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
          )
        }
      }
      Vue.set(parentState, moduleName, module.state)
    })
  }

  const local = module.context = makeLocalContext(store, namespace, path)

  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })

  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key
    const handler = action.handler || action
    registerAction(store, type, handler, local)
  })

  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })
  //这里递归调用自己
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}
function registerGetter (store, type, rawGetter, local) {
  if (store._wrappedGetters[type]) {
    if (__DEV__) {
      console.error(`[vuex] duplicate getter key: ${type}`)
    }
    return
  }
  store._wrappedGetters[type] = function wrappedGetter (store) {
    return rawGetter(
      local.state, // local state
      local.getters, // local getters
      store.state, // root state
      store.getters // root getters
    )
  }
}
/**
 * 为了统一参数
 */
function unifyObjectStyle (type, payload, options) {
  //传入是一个对象
  if (isObject(type) && type.type) {
    options = payload
    payload = type
    type = type.type
  }

  // if (__DEV__) {
  //   assert(typeof type === 'string', `expects string as the type, but found ${typeof type}.`)
  // }

  return { type, payload, options }
}

function resetStoreVM (store, state, hot) {
  const oldVm = store._vm

  // bind store public getters
  store.getters = {}
  // reset local getters cache
  store._makeLocalGettersCache = Object.create(null)
  const wrappedGetters = store._wrappedGetters
  const computed = {}
  forEachValue(wrappedGetters, (fn, key) => {
    // use computed to leverage its lazy-caching mechanism
    // direct inline function use will lead to closure preserving oldVm.
    // using partial to return function with only arguments preserved in closure environment.
    computed[key] = partial(fn, store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })

  // use a Vue instance to store the state tree
  // suppress warnings just in case the user has added
  // some funky global mixins
  const silent = Vue.config.silent
  Vue.config.silent = true
  //这里实际就是添加了响应式,使用_vm与$$state是为了防止用户更改
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
  Vue.config.silent = silent

  // enable strict mode for new vm
  if (store.strict) {
    enableStrictMode(store)
  }
  if (oldVm) {
    if (hot) {
      // dispatch changes in all subscribed watchers
      // to force getter re-evaluation for hot reloading.
      store._withCommit(() => {
        oldVm._data.$$state = null
      })
    }
    Vue.nextTick(() => oldVm.$destroy())
  }
}
function registerMutation (store, type, handler, local) {
  const entry = store._mutations[type] || (store._mutations[type] = [])
  entry.push(function wrappedMutationHandler (payload) {
    handler.call(store, local.state, payload)
  })
}
function getNestedState (state, path) {
  return path.reduce((state, key) => state[key], state)
}
/**
 * 我要提供对外的install方法
 * install方法将Vuex装载到Vue对象上
 */
 export function install (_Vue) {
  //判断是否有vue实例
  if (Vue) {
    console.error(
      '[vuex] already installed. Vue.use(Vuex) should be called only once.'
    )
    return
  }
  //注册到全局所有方法都可以使用
  Vue = _Vue;
  //注入vue
  applyMixin(Vue)
}